package com.sharp.authorization.config.security;

import com.sharp.authorization.annotation.auth.Auth;
import com.sharp.authorization.annotation.auth.AuthRole;
import com.sharp.authorization.annotation.captcha.CaptchaVerify;
import com.sharp.authorization.annotation.msg.MsgCodeVerify;
import com.sharp.authorization.constants.common.RequestConst;
import com.sharp.authorization.holder.ClientTypeHolder;
import com.sharp.authorization.service.captcha.CaptchaService;
import com.sharp.authorization.service.sms.MsgVerificationService;
import com.sharp.authorization.service.token.TokenService;
import com.sharp.authorization.vo.captcha.CaptchaVerificationReq;
import com.sharp.authorization.vo.msg.MsgVerificationReq;
import com.sharp.framework.utils.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Title: AuthInterceptor
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2021/8/17 15:52
 */
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private TokenService tokenService;
    @Resource
    private MsgVerificationService msgVerificationService;
    @Resource
    private CaptchaService captchaService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        enableCrossDomain(request, response);
        ClientTypeHolder.set(request.getHeader(RequestConst.CLIENT_TYPE));
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Class<?> clazz = handlerMethod.getBeanType();
        Auth auth = null;

        if (method.isAnnotationPresent(Auth.class)) {
            auth = method.getAnnotation(Auth.class);
        } else if (clazz.isAnnotationPresent(Auth.class)) {
            auth = clazz.getDeclaredAnnotation(Auth.class);
        }
        if (auth != null) {
            for (Class<? extends AuthRole> authRoleClazz : auth.value()) {
                ((AuthRole) applicationContext.getBean(StringUtils.uncapitalize(authRoleClazz.getSimpleName()))).validate(request);
            }
        }

        if (method.isAnnotationPresent(CaptchaVerify.class)) {
            CaptchaVerify verify = method.getAnnotation(CaptchaVerify.class);
            if (StringUtils.isEmpty(verify.field())) {
                captchaVerify(request, verify.value());
            } else {
                captchaVerify(request, request.getParameter(verify.field()));
            }
        }
        if (method.isAnnotationPresent(MsgCodeVerify.class)) {
            MsgCodeVerify verify = method.getAnnotation(MsgCodeVerify.class);
            msgCodeVerify(request, verify.value());
        }

        return true;
    }

    private void enableCrossDomain(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> headersInfoMap = HttpUtil.getHeadersInfoMap(request);
        response.setHeader("Access-Control-Allow-Origin", headersInfoMap.get("origin"));
        response.setHeader("Access-Control-Allow-Headers", RequestConst.getAllHeader());
        response.setHeader("Access-Control-Allow-Methods", RequestConst.getAllMethod());
    }

    private void captchaVerify(HttpServletRequest request, String captchaVerifyType) {
        CaptchaVerificationReq req = HttpUtil.getParamMap(request, CaptchaVerificationReq.class);
        String token = tokenService.getToken();
        captchaService.validateCaptcha(token, captchaVerifyType, req.getGraphCode());
    }

    private void msgCodeVerify(HttpServletRequest request, String msgCodeVerifyType) {
        MsgVerificationReq req = HttpUtil.getParamMap(request, MsgVerificationReq.class);
        String token = tokenService.getToken();
        msgVerificationService.validateMsgCode(token, req, msgCodeVerifyType);
    }
}
